NodeJS 命令行程序和文件操作
命令行程序
读取控制台输入
文档示例
const readline = require('readline');
// 实现这个接口,传入标准输入输出流
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// 把输入当作参数传入回调函数
rl.question('你如何看待 Node.js 中文网?\n', (answer) => {
// TODO:将答案记录在数据库中。
console.log(`感谢您的宝贵意见: ${answer}`);
rl.close();
});
'line' 事件
每当 input 流接收到行尾输入(\n、 \r 或 \r\n)时就会触发 'line' 事件。 这种情况通常发生在当用户按下 <Enter>
键或 <Return>
键。
调用监听器函数时会带上包含接收到的那一行输入的字符串。
rl.on('line', (input) => {
console.log(`接收到: ${input}`);
});
关闭输入输出流
rl.close()
方法会关闭 readline.Interface
实例,并放弃对 input 和 output 流的控制;当调用时,将触发 'close'
事件。
但是调用 rl.close()
不会立即停止 readline.Interface
实例触发的 其他事件(例如 'line'
)。
采集系统实例
const readline = require('readline');
const fs = require('fs');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let persion = {
name: null,
gender: null,
birthday: null,
job: null
};
let index = 0;
const list = [
{
question: '请确认是否立即输入信息?(Y/N):',
handler: (answer) => {
answer = answer.trim();
if (answer === '' || answer.toLowerCase() === 'y') {
console.log('开始输入录入操作');
index++;
console.log(list[index].question);
} else if (answer.toLowerCase() === 'n') {
console.log('退出本次输入录入操作');
process.exit(0);
} else {
console.log('输入无效请重新输入');
}
}
},
{
question: '请输入用户名:[1-10字母]',
handler: (answer) => {
answer = answer.trim();
// 使用一个正则表达式字面量,其由包含在斜杠之间的模式组成
// 例如 var re = /ab+c/; 等价 var re = new RegExp("ab+c");
// 更多参考: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
if (/^[a-zA-Z]{1,10}$/.test(answer)) {
persion.name = answer;
index++;
console.log(list[index].question);
} else {
console.log('\n输入无效\n请输入长度1-10的字母\n');
}
}
},
{
question: '请输入性别 [数字] \n1. 男 \n2. 女',
handler: (answer) => {
answer = answer.trim();
if (/^[12]{1}$/.test(answer)) {
persion.gender = (answer == 1 ? '男' : '女');
index++;
console.log(list[index].question);
} else {
console.log('输入错误');
}
}
},
{
question: '请输入您的生日(如:2000-01-01)',
handler: (answer) => {
answer = answer.trim();
// 输入2000-5-5也正确
if ( /^(19|20)\d{2}-(1[0-2]|0?[1-9])-(0?[1-9]|[1-2][0-9]|3[0-1])$/.test(answer)) {
persion.birthday = answer;
index++;
console.log(list[index].question);
} else {
console.log('输入错误');
}
}
},
{
question: `请选择职业 [数字] \n0. 程序员 \n1. 学生 \n2. 老师 \n3. 美术 \n4. 其他`,
handler: (answer) => {
answer = answer.trim();
if (/^[0-5]{1}$/.test(answer)) {
let temp = ['程序员', '学生', '老师', '美术', '其他']
persion.job = temp[answer]
index++;
console.log(list[index].question);
} else {
console.log('输入错误');
}
}
},
{
question: `请确认是否保存信息?(Y/N):`,
handler: (answer) => {
answer = answer.trim();
if (answer === '' || answer.toLowerCase() === 'y') {
/* stringify:
第二个参数是处理序列化的(传入一个回调),null表示自动处理
第三个参数是 如果参数是个数字,它代表有多少的空格(如果传入字符串就取字符串长度,最大10)
*/
fs.writeFileSync('./' + persion.name + '.json', JSON.stringify(persion, null, 4));
console.log('保存完毕退出程序');
process.exit(0);
} else if (answer.toLowerCase() === 'n') {
console.log('不保存本次录入');
process.exit(0);
} else {
console.log('输入无效请重新输入');
}
}
},
];
rl.on('line', (answer) => {
list[index].handler(answer);
});
// 第一个问题要先抛出来
console.log(list[0].question);
标准命令行程序模块
一般开发有各种参数的命令行程序使用的是这个 commander
模块,可以给命令行程序写一个(参数)完整的帮助文档
# 安装依赖
npm install commander
设置启动方式
#! /usr/bin/env node
表示默认使用 NodeJS 来执行,不过在写 git 钩子时已经说了,Windows 平台不适应这个,Windows 是通过文件后缀来判断执行文件的
#! /usr/bin/env node
const commander = require('commander'); // include commander in git clone of commander repo
const program = new commander.Command();
program
.option('-d, --debug', 'output extra debugging')
.option('-s, --small', 'small pizza size')
.option('-p, --pizza-type <type>', 'flavour of pizza');
program.parse(process.argv);
if (program.debug) console.log(program.opts());
console.log('pizza details:');
if (program.small) console.log('- small pizza size');
if (program.pizzaType) console.log(`- ${program.pizzaType}`);
输入 -h
node "d:\javaScript_Project\studyExpress\01.js" -h
Usage: 01 [options]
Options:
-d, --debug output extra debugging
-s, --small small pizza size
-p, --pizza-type <type> flavour of pizza
-h, --help display help for command
输入其参数
node "d:\javaScript_Project\studyExpress\01.js" -d -s -p String
{ debug: true, small: true, pizzaType: 'String' }
pizza details:
- small pizza size
- String
进程相关
参考资料 中文文档--process
process 顾名思义 与进程相关的全局变量
// 打印一下这个对象就能知道包含了哪些东西了
console.log(process)
作用:它是用于描述当前 NodeJS 进程状态的对象,提供了一个与操作系统的简单接口。通常在写本地命令行程序的时候会用到
进程属性(读/修改) 进程方法(调用执行) 进程事件(监听处理)
beforeExit 事件
NodeJS退出之前会执行这个事件,回调函数的参数是退出状态码(就像 java的 -1表示非正常退出)
beforeExit事件与exit事件的主要区别是,beforeExit的监听函数可以部署异步任务,而exit不行
使用例:
process.on('beforeExit', (code) => {
console.log('进程 beforeExit 的退出状态码: ', code);
});
exit 事件
监听器函数必须只执行同步操作
process.on('exit', (code) => {
console.log(`退出码: ${code}`);
});
uncaughtException 事件
当有未捕获的异常抛出时,可以用这个做最后的补救
参数:
err <Error>
未捕获的异常
origin <string>
这个参数可以不添加 标明异常的 来源
process.on('uncaughtException', (err, origin) => {
fs.writeSync(
process.stderr.fd,
`捕获的异常: ${err}\n` +
`异常的来源: ${origin}`
);
// 再执行退出函数
// 0 表示正常退出
// 1 使用失败代码退出
process.exit(1)
});
setTimeout(() => {
console.log('这里仍然会运行');
}, 500);
// 故意引起异常,但不要捕获它。
nonexistentFunc();
console.log('这里不会运行');
文件全路径名和目录名
__filename
与 __dirname
用于返回当前模块的文件全路径名和目录名
console.log(__filename)
// 输出为: d:\javaScript_Project\studyExpress\01.js
console.log(__dirname)
// 输出为:d:\javaScript_Project\studyExpress
路径模块
const path = require("path")
// 格式化路径
console.log('normalization : ' + path.normalize('/test/test1//2slashes/1slash/tab/..'));
// 连接路径
console.log('joint path : ' + path.join('/test', 'test1', '2slashes/1slash', 'tab', '..'));
// 转换为绝对路径
console.log('resolve : ' + path.resolve('main.js'));
// 路径中文件的后缀名
console.log('ext name : ' + path.extname('main.js'));
// 输出
// normalization : /test/test1/2slashes/1slash
// joint path : /test/test1/2slashes/1slash
// resolve : /web/com/1427176256_27423/main.js
// ext name : .js
一般 path
模块配合上面的 __filename
与 __dirname
使用
console.log(path.join(__dirname, '/assets/temp.txt'));
文件操作
readFileSync 同步读取
这个 readFileSync
的原理就是全部读取到 buffer 里,所以需要使用 toString
const fs = require('fs');
// 同步读取文件的全部内容。 默认字符编码是UTF-8
const content = fs.readFileSync('./temp.txt');
console.log(content.toString());
因为这是同步读取,所以可以直接通过 try-Catch
来捕获异常
const fs = require('fs');
try {
const content = fs.readFileSync('./tep.txt');
console.log(content.toString());
} catch (error) {
console.log('错误原因:' + error.message + '\n 错误的位置: \n' + error.stack);
}
readFile 异步读取
注意 readFile
与上面的 readFileSync
区别就是 readFileSync
是同步的,readFile
是异步的;所以这个 readFile
通过回调函数处理异常
const fs = require('fs');
fs.readFile('./tem.txt', (err, content) => {
if(err){
console.log('错误原因:\n' + err.message + '\n 错误的位置: \n' + err.stack);
return;
}
console.log(content.toString());
})
因为这个 readFile
返回值是 Promise 对象,所以也可以使用 Promise 的形式进行异步编程
const fsp = require('fs').promises
fsp.readFile('./tep.txt')
.then(content => {
console.log(content.toString());
})
.catch(err => {
console.log('错误原因:\n' + err.message + '\n 错误的位置: \n' + err.stack);
})
亦或者使用 ES6 的新关键字 async/await
进行异步编程,这样还能使用 try-Catch
来捕获异常
const fsp = require('fs').promises;
const readFile = async ()=>{
try {
let constent = await fsp.readFile('./temp.txt');
console.log(constent.toString());
} catch (error) {
console.log('错误原因:\n' + err.message + '\n 错误的位置: \n' + err.stack);
}
};
readFile();
writeFile 异步写入
异步写入到文件里,不等待回调就对同一个文件多次使用 fs.writeFile()
是不安全的
fs.writeFile('文件.txt', 'Node.js 中文网', 'utf8', callback);
这个可以像上面那样进行异步操作
writeFileSync 同步写入
同步写入到文件里面
fs.writeFileSync(fileName, str, 'utf8');
可以使用 appendFile
对文件末尾追加内容